#!/usr/bin/python
#
# Smartcam Skeleton Merger
# Purpose: Merge skeleton from an already compiled smartcam program
#          to speed up execution.
# Author:  Wouter Caarls
# Date:    03-04-2007

import sys, copy, target, shutil, filecmp

from aterm import *
from cterm import *
from stratego import *

class Kernel:
  def __repr__(self):
    return "%s(%s) %s(%s) {%s}" % (self.skeleton, self.sparams, self.kernel, self.kparams, self.body)

class Callsite:
  def __repr__(self):
    return "%s @ %s: %s" % (self.call, self.line, self.kernel.kernel)

def getArgument(globarg, fp):
  a = target.AvailabilityArgument(globarg, fp.iddecl().id().name())
  storage = fp.typespec().storageclass().children
  spec = fp.typespec().typespec()
  
  if ATerm("Stream") in storage:
    a.type = "stream"
    if ATerm("In") in storage:
      a.intent = "in"
    elif ATerm("Out") in storage:
      a.intent = "out"
    else:
      raise "Stream argument of %s(%s) defined without direction" % (c.kernel.skeleton, c.kernel.kernel)
  elif ATerm("Lookup") in storage:
    a.type = "stream"
    a.intent = "in"
  else:
    if ATerm("In") in storage:
      a.intent = "in"
    elif ATerm("Out") in storage:
      a.intent = "out"
      
    if spec.name()[-4:-1] == "int":
      a.type = "int"
    elif spec.name()[-7:-1] == "double":
      a.type = "double"
    else:
      a.type = "pointer"

  a.datatype = spec.name()

  return a

if len(sys.argv) < 2:
  print "Usage: " + sys.argv[0] + " <basename>"
  sys.exit(-1)

sys.argv[1] = os.path.join(os.getcwd(), sys.argv[1])
(root, ext) = os.path.splitext(sys.argv[1])
os.chdir(os.path.dirname(sys.argv[0]))

streamprog = sys.argv[1] + ".sc"
clientprog = os.path.join(os.path.dirname(sys.argv[1]), "main.cpp")
serverprog = sys.argv[1] + ".c"
availability = sys.argv[1] + "-availability.xml" 
colocation = sys.argv[1] + "-colocation.xml" 
mergeprog = sys.argv[1] + "-merge.c" 

# Initialize targets
tclasses = target.targets()
targets = []
for t in tclasses:
  targets.append(t(root+"-merge", True))
  
print "Parsing " + streamprog

inputA = Parse(readall(open(streamprog)), "parse-p -p")
if inputA == None: sys.exit(1)

input = Transform(inputA, "strategies/strip-annos")

print "Parsing " + clientprog

main = Parse(readall(open(clientprog)), "parse-p")

print "Extracting typedefs"

typedefs = PPC(Transform(input, "strategies/extract-typedefs"))

print "Extracting skeletons"

# Extract skeleton/kernel combinations from input file
kernels = InterpretATerm(Transform(input, "strategies/extract-kernels")).children

# Fill a dictionary with them
kdict = dict()
for k in kernels:
  nk = Kernel()
  nk.skeleton = k.sdef().id().name()
  nk.kernel = k.kdef().id().name()
  nk.sparams = k.sdef().paramlist().paramdecls().children
  nk.kparams = k.kdef().paramlist().paramdecls().children
  nk.body = k.body()
  kdict[nk.kernel] = nk

print "Extracting sequences"

seqlist = Transform(inputA, "strategies/extract-sequences -a " + availability + " -c " + colocation + " -s " + "c")
availability = dict()
properties = ""

rpc = 192
    
for i in range(len(seqlist.children)):
  seq = seqlist.children[i]
  meta = getAnnotation(seq.children[0], "Meta").children[0].data
  arguments = []
  fc = []
  maxl = 0

  print "Processing sequence " + str(i+1) + " of " + str(len(seqlist.children)) + " (" + str(meta) + " metaskeleton)"

  #if len(seq.children) > 8:
  #  print "  Sequence too long"
  #  continue

  print "  Extracting parameters"

  for j in range(len(seq.children)):
    fun = seq.children[j]
    knl = seq.children[j].children[0].children[0].data[1:-1]
    lin = getAnnotation(fun, "Line").data
    par = getAnnotation(fun, "Parameters")

    if int(lin) > maxl:
      maxl = int(lin)

    for k in range(len(par.children)):
      if par.children[k].children[1].children != []:      
        cil = int(par.children[k].children[1].children[0].children[0].data)
      else:
        cil = 0
        
      cii = -1
        
      for j2 in range(len(seq.children)):
        if int(getAnnotation(seq.children[j2], "Line").data) == cil:
          cii = j2
        
      if cii == -1:
        arguments.append(getArgument(len(arguments), kdict[knl].kparams[k]))
        fc.append(fun.children[1].children[k])

  availability[rpc] = target.Availability(rpc, meta + "-multi-" + str(maxl), meta)
  availability[rpc].arguments = arguments

  success = False

  for t in targets:
  
    print "  " + str(t) + " target"
    
    print "    Building metaskeleton input"

    skelin = """
#include "%s/%s.s"

%s

int instances = %d;
instance_t instance[%d];
""" % (str(t), meta, typedefs, len(seq.children), len(seq.children))

    params = 0
    
    for j in range(len(seq.children)):
      fun = seq.children[j]
      skl = getAnnotation(fun, "Skeleton").data[1:-1]
      lin = getAnnotation(fun, "Line").data
      knl = seq.children[j].children[0].children[0].data[1:-1]
      par = getAnnotation(fun, "Parameters")
      
    
      print "      " + skl + "(" + knl + ") on line " + str(lin)
    
      instancestart = """
{
  int ii = %d;
  
  instance[ii].arguments = %d;
""" % (j, len(par.children))

      instancemid = ""

      instanceend = """
  {
    #include "data/%s-%s-%s.sig"
  
    build(instance[ii].argcode, &instance[ii].kernel);
  }
  
  instance[ii].skeleton = `%s`;
  instance[ii].body = `
{
  #include "%s/%s.s"
}
`;
""" % (skl, knl, lin, skl, str(t), skl)
      
      for k in range(len(par.children)):
        if par.children[k].children[1].children != []:      
          cil = int(par.children[k].children[1].children[0].children[0].data)
          cia = int(par.children[k].children[1].children[0].children[1].data) - 1
        else:
          cil = 0
          cia = 0
      
        cii = -1
        
        for j2 in range(len(seq.children)):
          if int(getAnnotation(seq.children[j2], "Line").data) == cil:
            cii = j2
        
        if cii == -1:
          cia = params
          params += 1
          
          instancemid += """
  instance[ii].argcode[%d].argid = `args[%d]`;
""" % (k, cia)
        
        instanceend += """
  instance[ii].argcode[%d].connector.instance = %d;
  instance[ii].argcode[%d].connector.argument = %d;
""" % (k, cii, k, cia)

      instanceend += """
}"""
    
      skelin += instancestart + instancemid + instanceend

    metaname = meta + "-multi-" + str(maxl)
    
    skelin = """
#include "stdsc.h"
#include "prop.h"

unsigned char operation[] = "%s";
unsigned char target[] = "%s";

int arguments = %d;
ilp_arg_t args[%d];

%s
  
propInit("data/%s-%s.xml");
%s(instance, instances);
propFini();
""" % (metaname, str(t), params, params, skelin, metaname, str(t), meta)

    print "    Calling C preprocessor"
    
    cppout = FreeTransform(FreeTransform(skelin, "cpp -Iinclude -I."), "grep -v \"^# \"")

    cppoutname = "data/" + metaname + "-" + str(t) + ".i"
    pepcioutname = "data/" + metaname + t.ext()

    f = open(cppoutname + "~", "w")
    print >>f, cppout
    f.close()    

    try:
      if filecmp.cmp(cppoutname, cppoutname + "~", False) == False:
        raise ValueError
      if os.path.getmtime(cppoutname) > os.path.getmtime(pepcioutname):
        raise ValueError
      print "      Using cached PEPCI result"
    except:
      shutil.copyfile(cppoutname + "~", cppoutname)
      print "      Running PEPCI"
      call = "$SC_TOP/src/pepci/src/pepci -i " + cppoutname + " -o " + pepcioutname + " -s $SC_TOP/src/pepci/strategies -d args -d scu_buf_t -d scu_bufptrid_t -d extents_t -d ilp_arg_t -d argcode_t -d log_default_streams_t -d ilpl -d op -d ilp_op_t -d lmem -d reg -d larg0 -d larg1 -d larg2 -d larg3 -d rarg0 -d rarg1 -d rarg2 -d rarg3 -a -c " + meta + "-" + str(maxl) + "-" + str(t) + " -lm -l../libprop/libprop.so -f"

      print call
        
      if os.system(call):
        print "Couldn't run PEPCI, skipping target"
        continue
        
    success = True

    availability[rpc].processors.append(target.AvailabilityProc(str(t), t.type()))
    f = open("data/%s-%s.xml" % (metaname, str(t)))
    properties += readall(f)
    f.close()
 
    f = open(pepcioutname, "r")
    skelout = readall(f)
    f.close()

    t.addkernel(rpc, meta, "multi", skelout)
    
  if success == False:
    print "No available targets, skipping sequence"
    continue
  
  print "  Replacing callsites"

  fmt = ""
  for a in availability[rpc].arguments:
    fmt += a.format()
    
  rpcparams = [ATerm("StringLit", [ATerm("[]", [ATerm("\"\\\"" + fmt + "\\\"\"")])]), \
               ATerm("IntConst", [ATerm("\"" + str(rpc) + "\"")])]
  rpcparams.extend(fc)
  
  repl = FunCall(Id("scOp" + str(len(fc))), ATerm("[]", rpcparams))

  for j in range(len(seq.children)):
    fun = seq.children[j]
    fid = getAnnotation(fun, "RPC").data[1:-1]

    if j == len(seq.children) - 1:
      main = Transform(ATerm("()", [main, ATerm(fid), repl]), "strategies/replace-rpc")
    else:
      main = Transform(ATerm("()", [main, ATerm(fid), ATerm("EmptyExp")]), "strategies/replace-rpc")
  
  rpc += 1

print "Outputting sources"

f = open(clientprog, "w")
print >>f, PPC(main)
f.close()

for t in targets:
  print "  " + str(t) + " target"
  t.write(typedefs)

print "Outputting availability"

av = open(sys.argv[1] + "-availability.xml", "r")   
avtxt = open(sys.argv[1] + "-availability.txt", "r")

prevav = readall(av)
prevavtxt = readall(avtxt)

av.close()
avtxt.close()

# Open availability file
av = open(sys.argv[1] + "-availability.xml", "w")
avtxt = open(sys.argv[1] + "-availability.txt", "w")
print >>av, "<document>"
print >>av, prevav
print >>av, properties
print >>av, "<operations>"
print >>avtxt, prevavtxt

for a in availability.values():
  print >>av, str(a)

  for p in a.processors:
    print >>avtxt, a.rpc, p.type

print >>av, "</operations>"
print >>av, "</document>"

av.close()
avtxt.close()
